package com.taikang.common.util.hibernate;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.springframework.util.Assert;

import com.taikang.common.util.BeanUtilsEx;

/**
 * Hibernate的范型基
 * 
 * 可以在service类中直接创建使用.也可以继承出DAO子类,在多个Service类中共享DAO操作.
 * 参�?Spring2.5自带的Petlinc例子,取消了HibernateTemplate.
 * 通过Hibernate的sessionFactory.getCurrentSession()获得session,直接使用Hibernate原生API.
 * 
 * @param <T>
 *            DAO操作的对象类
 * @param <PK>
 *            主键类型
 * 
 * */
@SuppressWarnings("unchecked")
public class SimpleHibernateTemplate<T, PK extends Serializable> {

	protected Logger logger = Logger.getLogger(getClass());

	protected SessionFactory sessionFactory;

	protected Class<T> entityClass;

	public SimpleHibernateTemplate(SessionFactory sessionFactory,
			Class<T> entityClass) {
		this.sessionFactory = sessionFactory;
		this.entityClass = entityClass;
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
		// return sessionFactory.openSession();
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void save(Object entity) {
		Assert.notNull(entity);
		getSession().saveOrUpdate(entity);
	}

	public void delete(T entity) {
		Assert.notNull(entity);
		getSession().delete(entity);
	}

	public void delete(PK id) {
		Assert.notNull(id);
		delete(get(id));
	}

	public List<T> findAll() {
		return findByCriteria();
	}

	public Page findAll(Page page) {
		return findByCriteria(page);
	}

	/**
	 * 按id获取对象.
	 */
	public T get(final PK id) {
		return (T) getSession().load(entityClass, id);
	}

	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param hql
	 *            hql语句
	 * @param values
	 *            数量可变的参�?
	 */
	public List find(String hql, Object... values) {
		return createQuery(hql, values).list();
	}

	/**
	 * Add by fee 20090908 按HQL及参数取任意大小List
	 * 
	 * @param start
	 * @param end
	 * @param hql
	 * @param values
	 * @return
	 */
	public List find(int start, int end, String hql, Object... values) {
		Query q = createQuery(hql, values);
		q.setFirstResult(start);
		q.setMaxResults(end);
		return q.list();
	}

	/**
	 * 按HQL分页查询. 暂不支持自动获取总结果数,�?��户另行执行查�?
	 * 
	 * @param page
	 *            分页参数.包括pageSize 和firstResult.
	 * @param hql
	 *            hql语句.
	 * @param values
	 *            数量可变的参�?
	 * 
	 * @return 分页查询结果,附带结果列表及所有查询时的参�?
	 */
	public Page find(Page page, String hql, Object... values) {
		Assert.notNull(page);
		Query q = createQuery(hql, values);
		q.setFirstResult(page.getStart());
		q.setMaxResults(page.getLimit());

		page.setResult(q.list());
		return page;
	}

	/**
	 * 按HQL查询唯一对象.
	 */
	public Object findUnique(String hql, Object... values) {
		return createQuery(hql, values).uniqueResult();
	}

	/**
	 * 按HQL查询Intger类形结果.
	 */
	public Integer findInt(String hql, Object... values) {
		return (Integer) findUnique(hql, values);
	}

	/**
	 * 按HQL查询Long类型结果.
	 */
	public Long findLong(String hql, Object... values) {
		return (Long) findUnique(hql, values);
	}

	/**
	 * 按Criterion查询对象列表.
	 * 
	 * @param criterion
	 *            数量可变的Criterion.
	 */
	public List findByCriteria(Criterion... criterion) {
		return createCriteria(criterion).list();
	}
	
	/**
	 * 按Criterion查询对象.
	 * add by fee 20100429
	 * @param criteria
	 *           
	 */
	  public T findByCriteria(Criteria criteria)
	  {
		  return (T) criteria.uniqueResult();
	  }

	/**
	 * 按Criterion分页查询.
	 * 
	 * @param page
	 *            分页参数.包括pageSize、firstResult、orderBy、asc、autoCount.
	 *            其中firstResult可直接指定也可以指定pageNo. autoCount指定是否动态获取总结果数.
	 * 
	 * @param criterion
	 *            数量可变的Criterion.
	 * @return 分页查询结果.附带结果列表及所有查询时的参数
	 */
	public Page findByCriteria(Page page, Criterion... criterion) {
		Assert.notNull(page);

		Criteria c = createCriteria(criterion);

		// page.setTotalCount(countQueryResult(page, c));
		// c.setFirstResult(page.getStart());
		// c.setMaxResults(page.getLimit());
		// page.setResult(c.list());
		return findByCriteria(page, c);
	}

	public Page findByCriteria(Page page, Criteria c) {
		Assert.notNull(page);

		page.setTotalCount(countQueryResult(page, c));
		c.setFirstResult(page.getStart());
		c.setMaxResults(page.getLimit());
		page.setResult(c.list());

		return page;
	}

	/**
	 * 按属性查找对象列�?
	 */
	public List<T> findByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		return createCriteria(Restrictions.eq(propertyName, value)).list();
	}

	/**
	 * 按属性查找唯�?���?
	 */
	public T findUniqueByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		return (T) createCriteria(Restrictions.eq(propertyName, value))
				.uniqueResult();
	}

	/**
	 * 根据查询函数与参数列表创建Query对象,后续可进行更多处�?辅助函数.
	 */
	public Query createQuery(String queryString, Object... values) {
		Assert.hasText(queryString);
		Query queryObject = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		return queryObject;
	}

	/**
	 * 根据Criterion条件创建Criteria,后续可进行更多处�?辅助函数.
	 */
	public Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 判断对象的属性�?在数据库内是否唯�?
	 * 
	 * 在修改对象的情景�?如果属�?新修改的�?value)等于属�?原�?(orgValue)则不作比�?
	 * 传回orgValue的设计侧重于从页面上发出Ajax判断请求的场�? 否则�?��SS2里那种以对象ID作为�?个参数的isUnique函数.
	 */
	public boolean isPropertyUnique(String propertyName, Object newValue,
			Object orgValue) {
		if (newValue == null || newValue.equals(orgValue))
			return true;

		Object object = findUniqueByProperty(propertyName, newValue);
		return (object == null);
	}

	/**
	 * 通过count查询获得本次查询�?��获得的对象
	 * 
	 * @return page对象中的totalCount
	 */
	protected int countQueryResult(Page page, Criteria c) {
		CriteriaImpl impl = (CriteriaImpl) c;

		// 先把Projection、ResultTransformer、OrderBy取出�?清空三�?后再执行Count操作
		Projection projection = impl.getProjection();
		ResultTransformer transformer = impl.getResultTransformer();

		List<CriteriaImpl.OrderEntry> orderEntries = null;
		try {
			orderEntries = (List) BeanUtilsEx.getFieldValue(impl,
					"orderEntries");
			BeanUtilsEx.setFieldValue(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			logger.error("不可能抛出的异常:{}", e);
		}

		// 执行Count查询
		int totalCount = 0;
		try {
			totalCount = (Integer) c.setProjection(Projections.rowCount())
					.uniqueResult();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		if (totalCount < 1)
			return -1;

		// 将之前的Projection和OrderBy条件重新设回�?
		c.setProjection(projection);

		if (projection == null) {
			c.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		if (transformer != null) {
			c.setResultTransformer(transformer);
		}

		try {
			BeanUtilsEx.setFieldValue(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			logger.error("不可能抛出的异常:{}", e);
		}

		return totalCount;
	}

	/**
	 * 通过sql语句更新数据
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	public int updateByNativeSql(String sql, Object... values) {
		SQLQuery sqlQuery = getSession().createSQLQuery(sql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				sqlQuery.setParameter(i, values[i]);
			}
		}
		return sqlQuery.executeUpdate();
	}

	/**
	 * 以map的形式返回对象列表
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	public List<Map> findByNativeSql(String sql, Object... values) {
		SQLQuery sqlQuery = doNativeSql(sql, values);
		return sqlQuery.list();

	}

	/**
	 * 以map的形式返回对象返回单个对象
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	public Map findUniqueByNativeSql(String sql, Object... values) {
		SQLQuery sqlQuery = doNativeSql(sql, values);
		return (Map) sqlQuery.uniqueResult();
	}

	private SQLQuery doNativeSql(String sql, Object... values) {
		SQLQuery sqlQuery = getSession().createSQLQuery(sql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				sqlQuery.setParameter(i, values[i]);
			}
		}
		sqlQuery.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
		return sqlQuery;
	}

	/**
	 * 以map的形式返回对象返回单个对象
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	public Long getSequenceNextValue(String seqName) {
		String sql = "select " + seqName + ".nextval as seq from dual";
		Map result = findUniqueByNativeSql(sql);
		BigDecimal dec = (java.math.BigDecimal) result.get("SEQ");
		return dec.longValue();
	}

}